说明
虽然我将这篇文章归到 HTML5 API 这一分类中,但确切的说 PWA 并不是单一API,而是对多种API的综合应用。特别的,一个完整的 PWA 需要两个特别的文件——服务线程(一般为 service-worker.js )和Web应用清单(一般为 manifest.json
或 manifest.webmanifest
),本文将主要对这两个文件展开介绍。
服务线程(service worker)
服务线程(service worker),是独立与页面的 WebWorker,其在启动后,会一直在后台运营(直到浏览器被关闭,或在一段时间内,没有该应用打开的的页面),并且可以拦截 Web 请求,将本地缓存甚至是即时生成的数据作为相应返回给原页面。
服务线程上下文环境
因为所有页面的请求可能都要经过服务线程,如果服务线程一但被堵塞,将会导致所有页面的请求都被堵塞,所以服务线程中的所有API均为异步API。
服务线程上下文环境是ServiceWorkerGlobalScope
的实例,除了ECMAScript定义的语法级对象外,主要还有以下几个属性:
self
: 指上下文环境本身,类似普通浏览器环境下的window
对象,继承自WorkerGlobalScope
caches
:Cache API
,同普通浏览器环境下的caches
对象,继承自WorkerGlobalScope
fetch
: 用于网络请求的函数,在服务线程中不支持XMLHttpRequest
,请用fetch
方法函数,继承自WorkerGlobalScope
,但进行了重载clients
: 其有打开窗口及获取已有窗口等的异步方法registration
: 当前服务线程的状态等skipWaiting
: 在服务线程脚本更新后,用于强制重起服务线程
另外有与windows
中同名同用法(或功能相似)的对象,均继承自WorkerGlobalScope
,具体见下表:
对象 | - | - | - |
---|---|---|---|
btoa |
location |
indexedDB |
addEventListener |
atob |
setTimeout |
navigator |
removeEventListener |
crypto |
setInterval |
performance |
dispatchEvent |
fonts |
clearTimeout |
importScripts |
createImageBitmap |
origin |
clearInterval |
queueMicrotask |
但因为上下文环境的不通,部分API不支持或缺少部分属性成员,如navigator
没有keyboard
、clipboard
、cookieEnabled
等成员。
事件
ServiceWorkerGlobalScope
主要有以下几个事件:
install
: 服务线程安装事件activate
: 服务线程激活事件fetch
: 页面或其他线程发起网络请求时的事件pushsubscriptionchange
: 推送订阅改变事件,属Push API
push
: 推送事件,属Push API
message
: 消息事件,postMessage
触发的事件notificationclick
: 通知点击事件,属Notification API
notificationclose
: 通知关闭事件,属Notification API
caches
ServiceWorkerGlobalScope.caches
是 CacheStorage
的实例,主要有以下几个方法:
match(request, options)
: 返回所有缓存库中第一个与请求匹配的Response
,返回值为Promise<Response>
open(name)
: 打开一个缓存库,返回值为Promise<Cache>
delete(name)
: 删除一个缓存库,返回值为Promise<boolean>
has(name)
: 判断是否存在指定的缓存库,返回值为Promise<boolean>
keys()
: 获取所有的缓存库名称,返回值为Promise<string[]>
Cache
实例
通过 ServiceWorkerGlobalScope.caches.open
得到一个 Cache
实例,所有的缓存操作均在此对象上实现。其实例主要有以下几个方法:
match(request, options)
: 返回缓存库中第一个与请求匹配的Response
,返回值为Promise<Response>
matchAll(request, options)
: 返回缓存库中所有与请求匹配的Response
,返回值为Promise<Response[]>
add(request)
: 将请求及对应的Response
加入到缓存库中,Response
将通过自动fetch
获得addAll(requests)
: 将多个请求及对应的Response
加入到缓存库中,Response
将通过自动fetch
获得put(request, response)
: 将指定的请求及对应相应关联后加入到缓存库中delete(request, options)
: 删除与请求匹配的缓存
install
事件与 activate
事件
由于事件是通过继承EventTarget
类实现的,所以,使用传统的方式,均会忽略事件回调函数返回的Promise对象,但在服务线程的安装、激活过程中,可能需要一些异步的操作,例如提前缓存部分页面。为确保触发install
、activate
事件后,能够保证相关操作完成后再结束这两种状态,install
事件与 activate
事件提供了 waitUntil
方法。
waitUntil
方法接收一个 Promise
对象作为参数,当该 Promise
对象状态转为 fulfilled
,rejected
后,install
、activate
状态才会结束,单如果不调用waitUntil
方法则直接结束。
fetch
事件
fetch
事件中可以劫持其他页面祸线程发起的网络请求,返回一个自定义的相应。
如果需要返回自定义请求,需要劫持原有请求,需要调用fetch
事件的 respondWith
方法。
respondWith
方法接收一个 Promise<Response>
对象作为参数,该Response
对象将作为原请求的Response
。
此Response
对象,可以是在fetch
事件中调用fetch
方法得到的Response
,也可以是caches
中缓存的Response
,甚至是可以通过调用Response
构造方法创建的Response
对象。
其他说明
目前,服务线程只能采用caches
和indexedDB
两种存储方式。
Web应用清单 (Web App Manifest)
Web应用清单主要提供有关应用程序的信息(例如名称、图标和描述)。本质为JSON文件,其MIME建议为application/manifest+json
其主要有以下几个属性:
name
: 必须,应用的名称short_name
: 应用的简称,一般用于主屏幕名称start_url
: 必须,应用的启动地址display
: 应用的像是方式, 目前支持:browser
: 按照常规网页打开,默认值minimal-ui
: 只保留控制导航的UI元素standalone
: 按照普通应用的形式显示fullscreen
: 全屏显示
background_color
: 浏览器自动生产的启动页的背景色,为 CSS颜色值description
: 应用的描述icons
: 必须,应用的图标,用于桌面图标及启动页面,是一个数组,每个成员有以下几个属性:src
: 必须,图标的地址sizes
: 必须,包含空格分隔图像尺寸的字符串type
: 图标的mine类型
orientation
: 显示方向,可以是以下值之一:any
natural
landscape
landscape-primary
landscape-secondary
portrait
portrait-primary
portrait-secondary
scope
: 定义此网站上下文的导航范围,如果超出这个范围将按照普通页面展示theme_color
: 网站的默认主题色
启动服务线程并加入清单文件
加入清单文件,只需要一个link
标签即可,如:
<link rel="manifest" href="/manifest.json">
启动服务线程需要通过JavaScript实现:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', {scope: '/'}).then(function (registration) {
// 注册成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function (err) {
// 注册失败 :(
console.log('ServiceWorker registration failed: ', err);
});
}